<?php
/**
 * Created by PhpStorm.
 * @author caon01
 * @date 2022/8/31 9:11 下午
 */

namespace App\Http\Controllers;


use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class ShortController extends BaseController
{
    public function list()
    {
        return view('short/list');
    }

    public function detail()
    {
        return view('short/detail');
    }

    public function getOne(Request $request)
    {
        $params = $request->all();
        if (empty($params['id'])) {
            response_json(1, '参数丢失，请联系管理员！');
        }

        $data = $this->getById($params['id']);

        response_json($data ? 0 : 1, $data ? '' : '系统异常，请联系管理员！', $data);
    }

    public function getById($id)
    {
        $data = Db::table('short_url')->where('id', $id)->get()
            ->map(function ($value){
                return (array)$value;
            })
            ->toArray();
        if ($data) {
            $data = current($data);
            $regions = Db::table('short_url_relation_region')->where(['short_url_id' => $data['id'], 'is_deleted' => 0])->get(['city'])->toArray();

            if ($regions) {
                $data['regions'] = array_column($regions, 'city');
            } else {
                $data['regions'] = [];
            }
        }

        return $data;
    }

    public function copy(Request $request)
    {
        $params = $request->all();
        if (empty($params['id'])) {
            response_json(1, '参数丢失，请联系管理员！');
        }

        $data = $this->getById($params['id']);
        if (!$data) {
            response_json(1, '复制失败，请联系管理员！');
        }

        $regions = $data['regions'];
        $data['route'] = $this->generateRoute();
        $data['title'] .= '-复制';
        unset($data['id']);
        unset($data['regions']);
        unset($data['created_on']);
        unset($data['created_by']);
        unset($data['modified_by']);
        unset($data['modified_on']);

        $id = Db::table('short_url')->insertGetId($data);

        if (!empty($data['type']) && $data['type'] == 'region') {
            if (!empty($regions)) {
                $rs = [];
                foreach ($regions as $region) {
                    $rs[] = [
                        'short_url_id' => $id,
                        'city' => $region,
                        'long_url' => $data['long_url'],
                    ];
                }

                Db::table('short_url_relation_region')->insert($rs);
            }
        }

        response_json($id ? 0 : 1, $id ? '复制成功' : '系统异常，请联系管理员！');
    }

    public function getList(Request $request)
    {
        $params = $request->all();
        $params['page_size'] = empty($params['page_size']) ? 15 : $params['page_size'];
        $params['current_page'] = empty($params['current_page']) ? 1 : $params['current_page'];

        $where = [['is_deleted', '=', '0']];
        if (!empty($params['keywords'])) {
            $where[] = ['title', 'like', "%{$params['keywords']}%"];
        }
        $data = DB::table('short_url')
            ->select('*')
            ->where($where)
            ->orderBy('created_on', 'desc')
            ->paginate($params['page_size'], ['*'], 'page', $params['current_page']);

        $list = [
            'total' => $data->total(),
            'current_page' => $data->currentPage(),
            'data' => json_decode(json_encode($data->items()), true),
        ];

        foreach ($list['data'] as $key => $row) {
            $list['data'][$key]['pv'] = 0;
            $list['data'][$key]['uv'] = 0;
            $list['data'][$key]['ip'] = 0;
            $logs = Db::table('access_log')->where(['short_url_id' => $row['id'], 'is_deleted' => 0])
                ->select(['agent', 'ip', 'long_url'])
                ->get()
                ->map(function ($value){
                    return (array)$value;
                })
                ->toArray();
            if ($logs) {
                $list['data'][$key]['pv'] = count($logs);
                $list['data'][$key]['ip'] = count(array_unique(array_column($logs, 'ip')));
                foreach ($logs as $log) {
                    if ($log['long_url'] == $row['long_url']) {
                        $list['data'][$key]['uv']++;
                    }
                }
            }
        }

        response_json(0, '', $list);
    }

    public function del(Request $request)
    {
        $params = $request->all();
        if (empty($params['id'])) {
            response_json(1, '参数有误，请联系管理员！');
        }
        $ret = Db::table('short_url')->where('id', $params['id'])->update(['is_deleted' => 1]);
        response_json($ret ? 0 : 1, $ret ? '删除成功！' : '删除失败，请联系管理员！');
    }

    public function saveData(Request $request)
    {
        $params = $request->all();

        // 数据校验
        $data = [];
        if (empty($params['title'])) {
            response_json(1, '缺失参数"标题"');
        }
        if (empty($params['route'])) {
            response_json(1, '缺失参数"短链接"');
        }

        $old = Db::table('short_url')->where(['route' => $params['route'], 'is_deleted' => 0])->first();
        if (empty($params['id']) || $old->id != $params['id']) {
            response_json(1, '"短链接"已被使用，请更换一个短链接');
        }

        if (empty($params['long_url'])) {
            response_json(1, '缺失参数"跳转链接"');
        }
        if (!empty($params['type'])) {
            $data['type'] = $params['type'];
        }
        if (!empty($params['default_url'])) {
            if (!preg_match("/^((https|http)?:\/\/)[^\s]+$/", $params['default_url'])) {
                response_json(1, '不满足条件跳转链接格式有误');
            }
        }
        if (!empty($params['type'])) {
            $data['type'] = $params['type'];
        }
        if (!empty($data['type']) && $data['type'] == 'region' && empty($params['regions'])) {
            response_json(1, '请输入指定省份');
        }

        if (!preg_match("/^((https|http)?:\/\/)[^\s]+$/", $params['long_url'])) {
            response_json(1, '跳转链接格式有误');
        }
        $data['title'] = trim($params['title']);
        $data['route'] = trim($params['route']);
        $data['long_url'] = trim($params['long_url']);

        $data['remark'] = empty($params['remark']) ? '' : $params['remark'];
        $data['default_url'] = empty($params['default_url']) ? '' : $params['default_url'];
        $data['domain'] = empty($params['domain']) ? '' : $params['domain'];
        $data['is_allow_qq'] = empty($params['is_allow_qq']) ? 0 : 1;
        $data['is_allow_wx'] = empty($params['is_allow_wx']) ? 0 : 1;
        $data['is_allow_pc'] = empty($params['is_allow_pc']) ? 0 : 1;
        $data['is_allow_mobile_defalut_browser'] = empty($params['is_allow_mobile_defalut_browser']) ? 0 : 1;
        $data['is_allow_android'] = empty($params['is_allow_android']) ? 0 : 1;
        $data['is_allow_apple'] = empty($params['is_allow_apple']) ? 0 : 1;
        $data['modified_on'] = date('Y-m-d H:i:s');

        if (isset($params['id'])) {
            $id = $params['id'];
            $ret = Db::table('short_url')->where('id', $params['id'])->update($data);
        } else {
            $id = $ret = Db::table('short_url')->insertGetId($data);
        }

        if (!empty($data['type']) && $data['type'] == 'region') {
            Db::table('short_url_relation_region')->where(['short_url_id' => $id, 'is_deleted' => 0])->update(['is_deleted' => 1]);

            if (!empty($params['regions']) && is_array($params['regions'])) {
                $regions = [];
                foreach ($params['regions'] as $region) {
                    $regions[] = [
                        'short_url_id' => $id,
                        'city' => $region,
                        'long_url' => $data['long_url'],
                    ];
                }

                Db::table('short_url_relation_region')->insert($regions);
            }
        }

        // 删除缓存
        Cache::forget('route:' . $data['route']);

        response_json($ret ? 0 : 1, $ret ? '保存成功！' : '保存失败，请联系管理员！', ['id' => $id]);
    }

    public function add(Request $request)
    {
        $params = $request->all();

        // 数据校验
        if (empty($params['long_url'])) {
            response_json(1, '缺失参数"跳转链接"');
        }

        if (!preg_match("/^((https|http)?:\/\/)[^\s]+$/", $params['long_url'])) {
            response_json(1, '跳转链接格式有误');
        }

        $route = $this->generateRoute();
        if (!$route) {
            response_json(1, '新增失败，请重试');
        }

        $data['long_url'] = trim($params['long_url']);
        $data['title'] = $data['route'] = $route;

        $id = $ret = Db::table('short_url')->insertGetId($data);

        response_json($ret ? 0 : 1, $ret ? '保存成功！' : '保存失败，请联系管理员！', ['id' => $id]);
    }

    public function generateRoute($times = 5)
    {
        if ($times < 1) {
            return '';
        }

        $route = static::random();
        $old = Db::table('short_url')->where(['route' => $route, 'is_deleted' => 0])->first();
        if ($old) {
            return $this->generateRoute($times--);
        }

        return $route;
    }

    public function getAccessLog(Request $request)
    {
        $params = $request->all();

        if (empty($params['id'])) {
            response_json(1, '参数有误，请联系管理员！');
        }

        $params['page_size'] = empty($params['page_size']) ? 15 : $params['page_size'];
        $params['current_page'] = empty($params['current_page']) ? 1 : $params['current_page'];

        $where = [['is_deleted', '=', '0'], ['short_url_id', '=' ,$params['id']]];
        $data = DB::table('access_log')
            ->select('*')
            ->where($where)
            ->orderBy('created_on', 'desc')
            ->paginate($params['page_size'], ['*'], 'page', $params['current_page']);

        response_json(0, '', $data);
    }

    public static function random($type = 'alnum', $length = 4)
    {
        switch ($type) {
            case 'alnum':
                $pool = '0123456789abcdefghijklmnopqrstuvwxyz';
                break;
            case 'alpha':
                $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
                break;
            case 'hexdec':
                $pool = '0123456789abcdef';
                break;
            case 'numeric':
                $pool = '0123456789';
                break;
            case 'nozero':
                $pool = '123456789';
                break;
            case 'distinct':
                $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
                break;
            default:
                $pool = (string) $type;
                break;
        }

        $crypto_rand_secure = function ($min, $max) {
            $range = $max - $min;
            if ($range < 0) {
                return $min;
            }
            // not so random...
            $log    = log($range, 2);
            $bytes  = (int) ($log / 8) + 1; // length in bytes
            $bits   = (int) $log + 1; // length in bits
            $filter = (int) (1 << $bits) - 1; // set all lower bits to 1
            do {
                $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
                $rnd = $rnd & $filter; // discard irrelevant bits
            } while ($rnd >= $range);
            return $min + $rnd;
        };

        $token = "";
        $max   = strlen($pool);
        for ($i = 0; $i < $length; $i++) {
            $token .= $pool[$crypto_rand_secure(0, $max)];
        }
        return $token;
    }

    public function redirect($route) {
        if (empty($route) || $this->isAntispam()) {
            echo '';die;
        }

        $route = trim($route);

        $data = Cache::get('route:' . $route);
        if (!$data) {
            $data = Db::table('short_url')->where(['route' => $route, 'is_deleted' => 0])->get()
                ->map(function ($value){
                    return (array)$value;
                })
                ->toArray();
            if (!$data) {
                echo '';die;
            }

            $data = $data[0];

            Cache::put('route:' . $route, json_encode($data), 3600);
        } else {
            $data = json_decode($data, true);
        }

        $defaultUrl = $data['default_url'] ?  : '';
        $url = $data['long_url'];

        // 调试
//        if ($this->isAndroid()) {
//            echo 'isAndroid';die;
//        }
//        if ($this->isMobile()) {
//            echo 'isMobile';die;
//        }
//        if ($this->isIos()) {
//            echo 'isIos';die;
//        }
//        if ($this->isWx()) {
//            echo 'isWx';die;
//        }
//        if ($this->isQq()) {
//            echo 'isQq';die;
//        }
//        if (!$this->isMobile()) {
//            echo '!isMobile';die;
//        }

        $pro = static::getRemoteCity($_SERVER['REMOTE_ADDR']);

        if ($data['type'] == 'region') {
            $regions = Db::table('short_url_relation_region')->where(['short_url_id' => $data['id'], 'is_deleted' => 0])->get(['city'])->toArray();

            $pros = implode(',', array_column($regions,'city'));
            if ($pro == '未知' || strpos($pros, $pro) !== false) {
                if (!$defaultUrl) {
                    echo '';die;
                } else {
                    $url = $defaultUrl;
                }
            }
        }

        if (!$data['is_allow_pc'] && !$this->isMobile()) {
            if (!$defaultUrl) {
                echo '';die;
            } else {
                $url = $defaultUrl;
            }
        }

        if (!$data['is_allow_qq'] && $this->isQq()) {
            if (!$defaultUrl) {
                echo '';die;
            } else {
                $url = $defaultUrl;
            }
        }

        if (!$data['is_allow_wx'] && $this->isWx()) {
            if (!$defaultUrl) {
                echo '';die;
            } else {
                $url = $defaultUrl;
            }
        }

        if (!$data['is_allow_android'] && $this->isAndroid()) {
            if (!$defaultUrl) {
                echo '';die;
            } else {
                $url = $defaultUrl;
            }
        }

        if (!$data['is_allow_apple'] && $this->isIos()) {
            if (!$defaultUrl) {
                echo '';die;
            } else {
                $url = $defaultUrl;
            }
        }

        $log = [
            'city' => $pro,
            'ip' => $_SERVER['REMOTE_ADDR'],
            'short_url_id' => $data['id'],
            'long_url' => $url,
            'client' => $this->getClient(),
            'agent' => $_SERVER['HTTP_USER_AGENT'],
        ];
        Db::table('access_log')->insert($log);

        header('Location: ' . $url);
    }

    public function getClient()
    {
        if (!$this->isMobile()) {
            return 'pc';
        }

        if ($this->isIos()) {
            return 'ios';
        }

        if ($this->isAndroid()) {
            return 'android';
        }

        return 'other';
    }

    public function isAntispam()
    {
        $agent = $_SERVER['HTTP_USER_AGENT'];

        return strpos($agent, 'antispam') !== false || strpos($agent, 'Dalvik') !== false ;
    }

    public function isMobile()
    {
        // 如果有HTTP_X_WAP_PROFILE则一定是移动设备
        if (isset ($_SERVER['HTTP_X_WAP_PROFILE']))
        {
            return true;
        }
        // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
        if (isset ($_SERVER['HTTP_VIA']))
        {
            // 找不到为flase,否则为true
            return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
        }
        // 脑残法，判断手机发送的客户端标志,兼容性有待提高
        if (isset ($_SERVER['HTTP_USER_AGENT']))
        {
            $clientkeywords = array ('nokia',
                'sony',
                'ericsson',
                'mot',
                'samsung',
                'htc',
                'sgh',
                'lg',
                'sharp',
                'sie-',
                'philips',
                'panasonic',
                'alcatel',
                'lenovo',
                'iphone',
                'ipod',
                'blackberry',
                'meizu',
                'android',
                'netfront',
                'symbian',
                'ucweb',
                'windowsce',
                'palm',
                'operamini',
                'operamobi',
                'openwave',
                'nexusone',
                'cldc',
                'midp',
                'wap',
                'mobile'
            );
            // 从HTTP_USER_AGENT中查找手机浏览器的关键字
            if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT'])))
            {
                return true;
            }
        }
        // 协议法，因为有可能不准确，放到最后判断
        if (isset ($_SERVER['HTTP_ACCEPT']))
        {
            // 如果只支持wml并且不支持html那一定是移动设备
            // 如果支持wml和html但是wml在html之前则是移动设备
            if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html'))))
            {
                return true;
            }
        }
        return false;
    }

    public function isIos()
    {
        $agent = $_SERVER['HTTP_USER_AGENT'];

        return strpos($agent, 'iPhone') || strpos($agent, 'iPad');
    }

    public function isAndroid()
    {
        $agent = $_SERVER['HTTP_USER_AGENT'];

        return strpos($agent, 'Android');
    }

    public function isWx()
    {
        $agent = $_SERVER['HTTP_USER_AGENT'];

        return strpos($agent, 'MicroMessenger');
    }

    public function isQq()
    {
        $agent = $_SERVER['HTTP_USER_AGENT'];

        // MQQBrowser
        return strpos($agent, 'QQ') && strpos($agent, '_SQ_');
    }

    public static function getRemoteCity($ip = '47.112.103.20')
    {
        try {
//            $result = file_get_contents('http://whois.pconline.com.cn/ipJson.jsp?ip=' . $ip . '&json=true');
//            $result = iconv('GB2312', 'UTF-8', $result);
//            $result = json_decode($result, true);
//            return empty($result['pro']) ? '未知' : $result['pro'];

            $result = file_get_contents('https://api.ip138.com/ip/?ip=' . $ip . '&datatype=xml&callback=find&token=ef4754678829c21ad8c758a92776bd93');
            $result = static::xmlToArr($result);
            return empty($result['data']['region']) ? '未知' : $result['data']['region'];
        } catch (\Exception $e) {
            return '北京市';
        }
    }

    public static function getRemoteCountry($ip = '47.112.103.20')
    {
        try {
            $result = file_get_contents('https://api.ip138.com/ip/?ip=' . $ip . '&datatype=xml&callback=find&token=ef4754678829c21ad8c758a92776bd93');
            $result = static::xmlToArr($result);
            return empty($result['data']['country']) ? '未知' : $result['data']['country'];
        } catch (\Exception $e) {
            return '北京市';
        }
    }

    private static function xmlToArr($xml)
    {
        if (empty($xml)) {
            return [];
        }
        $xml = htmlspecialchars_decode($xml);
        $xml = simplexml_load_string($xml);
        $xml = json_decode(json_encode($xml), true);

        return $xml;
    }
}
